package com.ft.turorial.spring.boot.security.shiro.realm;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.ft.turorial.spring.boot.domain.Role;
import com.ft.turorial.spring.boot.domain.User;
import com.ft.turorial.spring.boot.security.SecurityConstants;
import com.ft.turorial.spring.boot.security.shiro.authc.UsernamePasswordCaptchaToken;
import com.ft.turorial.spring.boot.security.shiro.exception.CaptchaException;
import com.ft.turorial.spring.boot.security.vo.UserStatus;
import com.ft.turorial.spring.boot.service.RoleService;
import com.ft.turorial.spring.boot.service.UserService;
import com.ft.turorial.spring.boot.web.util.SessionUtil;

public class UserRealm extends AuthorizingRealm{

	@Resource
    private UserService userService;
	@Resource
	private RoleService roleService;
	
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		String currentLoginName = (String)principals.getPrimaryPrincipal();
		List<String> userRoles = new ArrayList<String>();  
		List<String> userPermissions = new ArrayList<String>();  
		//从数据库中获取当前登录用户的详细信息  
		User user = userService.findByLoginName(currentLoginName);
		if(null != user){  
			//获取当前用户下所有ACL权限列表  待续。。。
			//获取当前用户下拥有的所有角色列表
			List<Role> roles = roleService.findByUserId(user.getId());
			for (int i = 0; i < roles.size(); i++) {
				userRoles.add(roles.get(i).getCode());
			}
		}else{  
			throw new AuthorizationException();  
		}  
		 System.out.println("#######获取角色："+userRoles);
		 System.out.println("#######获取权限："+userPermissions);
		//为当前用户设置角色和权限  
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRoles(userRoles);
        authorizationInfo.addStringPermissions(userPermissions); 
        return authorizationInfo;
	}

	/** 
     * 验证当前登录的Subject
     * LoginController.login()方法中执行Subject.login()时 执行此方法 
     */ 
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		System.out.println("###【开始认证[SessionId]】"+SecurityUtils.getSubject().getSession().getId());
		UsernamePasswordCaptchaToken token = (UsernamePasswordCaptchaToken) authcToken;
		String loginName = (String)token.getPrincipal();
		if (loginName == null) {
			throw new AccountException("Null usernames are not allowed by this realm.");
		}
		//增加验证码判断逻辑
		//  输入的验证码
		String captcha = token.getCaptcha();
		//  系统产生的验证码
		String outputCaptcha = (String) SecurityUtils.getSubject().getSession()
						.getAttribute(SecurityConstants.SESSION_KEY_CAPTCHA);
		if (null == captcha || !captcha.equalsIgnoreCase(outputCaptcha)) {
			throw new CaptchaException("验证码输入错误");
		}
		
        User user = userService.findByLoginName(loginName);
        if(user == null) {
            throw new UnknownAccountException();//没找到帐号
        }
        if(user.getStatus()==UserStatus.LOCKED) {
            throw new LockedAccountException(); //帐号锁定
        }
        if(user.getStatus()==UserStatus.DISABLE) {
        	throw new DisabledAccountException();//账号禁用
        }
        //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配，如果觉得人家的不好可以自定义实现
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getUserName(), //用户名
                user.getPassword(), //密码
                ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt,采用明文（例如Oauth2.0认证时的code）访问时，不需要此句
                getName()  //realm name
        );
        SessionUtil.setUser(user);
        return authenticationInfo;
	}

	@Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }

    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }
    /**
	 * 自定义Authentication对象，使得Subject除了携带用户的登录名外还可以携带更多信息.
	 */
	public static class ShiroUser implements Serializable {

		private static final long serialVersionUID = -1748602382963711884L;
		private String loginName;
		private String name;

		public ShiroUser(String loginName, String name) {
			this.loginName = loginName;
			this.name = name;
		}

		public String getLoginName() {
			return loginName;
		}

		/**
		 * 本函数输出将作为默认的<shiro:principal>输出.
		 */
		@Override
		public String toString() {
			return loginName;
		}

		public String getName() {
			return name;
		}
	}
}
